﻿#region Using
using System;
using System.IO;
using System.Drawing;
using Awesomium.Core;
using System.Diagnostics;
using System.Windows.Forms;
using Awesomium.Windows.Forms;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
#endregion

namespace IGGWindowApp
{
    public partial class WebPanel : Panel, IResourceInterceptor
    {
        #region Fields
        private WebView webView;
        private ImageSurface surface;
        private bool needsResize;
        private WebSession session;
        private BindingSource bindingSource;
        #endregion

        private const string LOGO_RESOURCE = "WinFormsSample.awe_logo_txt_light.png";

        public void LoadURL(string url)
        {
            webView.Source = new Uri(url);
        }

        public WebPanel()
        {
            if (!WebCore.IsRunning)
                WebCore.Initialize(WebConfig.Default);

            session = WebCore.CreateWebSession(
                String.Format("{0}{1}Cache", Path.GetDirectoryName(Application.ExecutablePath), Path.DirectorySeparatorChar),
                WebPreferences.Default);

            Debug.Print(WebCore.Version.ToString());

            // Notice that 'Control.DoubleBuffered' has been set to true
            // in the designer, to prevent flickering.

            InitializeComponent();

            // Initialize the view.
            InitializeView(WebCore.CreateWebView(this.ClientSize.Width, this.ClientSize.Height, session));
        }

        private void InitializeView(WebView view)
        {
            if (view == null)
                return;

            WebCore.ResourceInterceptor = this;

            // Create an image surface to render the
            // WebView's pixel buffer.
            surface = new ImageSurface();
            surface.Updated += OnSurfaceUpdated;

            webView = view;

            // Assign our surface.
            webView.Surface = surface;

            // Handle some important events.
            webView.CursorChanged += OnCursorChanged;
            //webView.TitleChanged += OnTitleChanged;

            bindingSource = new BindingSource() { DataSource = webView };
            this.DataBindings.Add(new Binding("Text", bindingSource, "Title", true));

            //webView.AddressChanged += OnAddressChanged;
            webView.ShowCreatedWebView += OnShowNewView;
            webView.Crashed += OnCrashed;

            // Load a URL, if this is not a child view.
            if (webView.ParentView == null)
                // Tip: /ncr = No Country Redirect ;-)
                webView.Source = new Uri("http://www.google.com/ncr");

            // Give focus to the view.
            webView.FocusView();
        }

        public void ResizeView()
        {
            //if ((webView == null) || !webView.IsLive)
            //    return;

            //if (needsResize)
            //{
                // Request a resize.
                webView.Resize(this.ClientSize.Width, this.ClientSize.Height);
                needsResize = false;
            //}
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if ((surface != null) && (surface.Image != null))
                e.Graphics.DrawImageUnscaled(surface.Image, 0, 0);
            else
                base.OnPaint(e);
        }

        //protected override void OnActivated(EventArgs e)
        //{
        //    base.OnActivated(e);
        //    this.Opacity = 1.0D;

        //    if ((webView == null) || !webView.IsLive)
        //        return;

        //    Debug.Print(webView.Source.AbsoluteUri);

        //    webView.FocusView();
        //}

        //protected override void OnDeactivate(EventArgs e)
        //{
        //    base.OnDeactivate(e);

        //    if ((webView == null) || !webView.IsLive)
        //        return;

        //    // Let popup windows be semi-transparent,
        //    // when they are not active.
        //    if (webView.ParentView != null)
        //        this.Opacity = 0.8D;

        //    webView.UnfocusView();
        //}

        public void CloseIt()
        {
            // Get if this is form hosting a child view.
            bool isChild = webView.ParentView != null;

            // Destroy the WebView.
            if (webView != null)
                webView.Dispose();

            // The surface that is currently assigned to the view,
            // does not need to be disposed. It will be disposed 
            // internally.

            // Shut down the WebCore last.
            if (!isChild)
                WebCore.Shutdown();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            if ((webView == null) || !webView.IsLive)
                return;

            if (this.ClientSize.Width > 0 && this.ClientSize.Height > 0)
                needsResize = true;

            // Request resize, if needed.
            this.ResizeView();
        }

        protected override void OnKeyPress(KeyPressEventArgs e)
        {
            base.OnKeyPress(e);

            if ((webView == null) || !webView.IsLive)
                return;

            webView.InjectKeyboardEvent(e.GetKeyboardEvent());
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            base.OnKeyDown(e);

            if ((webView == null) || !webView.IsLive)
                return;

            webView.InjectKeyboardEvent(e.GetKeyboardEvent(WebKeyboardEventType.KeyDown));
        }

        protected override void OnKeyUp(KeyEventArgs e)
        {
            base.OnKeyUp(e);

            if ((webView == null) || !webView.IsLive)
                return;

            webView.InjectKeyboardEvent(e.GetKeyboardEvent(WebKeyboardEventType.KeyUp));
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);

            if ((webView == null) || !webView.IsLive)
                return;

            webView.InjectMouseDown(e.Button.GetMouseButton());
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);

            if ((webView == null) || !webView.IsLive)
                return;

            webView.InjectMouseUp(e.Button.GetMouseButton());
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if ((webView == null) || !webView.IsLive)
                return;

            webView.InjectMouseMove(e.X, e.Y);
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);

            if ((webView == null) || !webView.IsLive)
                return;

            webView.InjectMouseWheel(e.Delta, 0);
        }

        private void OnTitleChanged(object sender, TitleChangedEventArgs e)
        {
            // Reflect the page's title to the window text.
            this.Text = e.Title;
        }

        private void OnAddressChanged(object sender, UrlEventArgs e)
        {
            // Reflect the current URL to the window text.
            // Normally, after the page loads, we will get a title.
            // But a page may as well not specify a title.
            this.Text = e.Url.ToString();
        }

        private void OnCursorChanged(object sender, CursorChangedEventArgs e)
        {
            Debug.Print("UI Thread: " + Thread.CurrentThread.ManagedThreadId);
            // Update the cursor.
            this.Cursor = Awesomium.Windows.Forms.Utilities.GetCursor(e.CursorType);
        }

        private void OnSurfaceUpdated(object sender, SurfaceUpdatedEventArgs e)
        {
            // When the surface is updated, invalidate the 'dirty' region.
            // This will force the form to repaint that region.
            Invalidate(e.DirtyRegion.ToRectangle(), false);
        }


        private void OnShowNewView(object sender, ShowCreatedWebViewEventArgs e)
        {
            if ((webView == null) || !webView.IsLive)
                return;

            if (e.IsPopup)
            {
                //// Create a WebView wrapping the view created by Awesomium.
                //WebView view = new WebView(e.NewViewInstance);
                //// ShowCreatedWebViewEventArgs.InitialPos indicates screen coordinates.
                //Rectangle screenRect = e.InitialPos.ToRectangle();
                //// Create a new WebForm to render the new view and size it.
                //WebForm childForm = new WebForm(view, screenRect.Width, screenRect.Height)
                //{
                //    ShowInTaskbar = false,
                //    FormBorderStyle = FormBorderStyle.FixedToolWindow,
                //    ClientSize = screenRect.Size
                //};

                //// Show the form.
                //childForm.Show(this);
                //// Move it to the specified coordinates.
                //childForm.DesktopLocation = screenRect.Location;
            }
            else
            {
                // Let the new view be destroyed. It is important to set Cancel to true 
                // if you are not wrapping the new view, to avoid keeping it alive along
                // with a reference to its parent.
                e.Cancel = true;

                // Load the url to the existing view.
                webView.Source = e.TargetURL;
            }
        }

        private void OnCrashed(object sender, CrashedEventArgs e)
        {
            Debug.Print("Crashed! Status: " + e.Status);
        }

        ResourceResponse IResourceInterceptor.OnRequest(ResourceRequest request)
        {
            Debug.Print("I/O Thread: " + Thread.CurrentThread.ManagedThreadId);

            if (request.Url.Segments.Length > 1)
            {
                // Get the last segment of the Uri. This is usually the file name.
                string fileName = request.Url.Segments[request.Url.Segments.Length - 1];

                Debug.Print("Possible file-name: " + fileName);

                // Check if this is a request for 'logo3w.png' (this is currently 
                // the name of the 'Google' image).
                if (String.Compare(fileName, "logo3w.png", true) == 0)
                {
                    // Get the embedded resource of the Awesomium logo.
                    var assembly = Assembly.GetExecutingAssembly();
                    var info = assembly.GetManifestResourceInfo(LOGO_RESOURCE);

                    // The resource does not exist.
                    if (info == null)
                        return null;

                    using (var stream = assembly.GetManifestResourceStream(LOGO_RESOURCE))
                    {
                        // Get a byte array of the resource.
                        byte[] buffer = new byte[stream.Length];
                        stream.Read(buffer, 0, buffer.Length);

                        // Initialize unmanaged memory to hold the array.
                        int size = Marshal.SizeOf(buffer[0]) * buffer.Length;
                        IntPtr pnt = Marshal.AllocHGlobal(size);

                        try
                        {
                            // Copy the array to unmanaged memory.
                            Marshal.Copy(buffer, 0, pnt, buffer.Length);

                            // Alternatively, you can pin the array in the managed heap.
                            // Note however that pinning objects seriously disrupts GC operation. 
                            // Being able to move objects around in the heap is one of the reasons 
                            // why modern GCs can (somewhat) keep up with manual memory management. 
                            // By pinning objects in the managed heap, the GC looses it's one 
                            // performance advantage over manual memory management: 
                            // a relatively un-fragmented heap.
                            //GCHandle handle = GCHandle.Alloc( buffer, GCHandleType.Pinned );
                            //IntPtr pnt = handle.AddrOfPinnedObject();

                            // Create a ResourceResponse. A copy is made of the supplied buffer.
                            return ResourceResponse.Create((uint)buffer.Length, pnt, "image/png");
                        }
                        finally
                        {
                            // Data is not owned by the ResourceResponse. A copy is made 
                            // of the supplied buffer. We can safely free the unmanaged memory.
                            Marshal.FreeHGlobal(pnt);
                        }
                    }
                }
            }

            // Return NULL to allow normal behavior.
            return null;
        }
    }
}
